package cn.com.dhcc.ddi.monitor.service;

import java.io.IOException;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import javax.script.ScriptException;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.com.dhcc.app.core.exception.DaoException;
import cn.com.dhcc.app.core.exception.JmxException;
import cn.com.dhcc.app.core.exception.NiceException;
import cn.com.dhcc.app.core.exception.ServiceException;
import cn.com.dhcc.app.core.handler.dbhandler.SingleTransationCircleWithOutResult;
import cn.com.dhcc.app.core.handler.modulehandler.WhereCondition;
import cn.com.dhcc.app.core.jmx.AbstractMonitorService;
import cn.com.dhcc.app.core.jmx.DefaultJvmMonitor;
import cn.com.dhcc.app.core.jmx.JmxConnectionFactory.JmxType;
import cn.com.dhcc.app.core.jmx.vo.JvmMonitor;
import cn.com.dhcc.app.core.jmx.vo.OsMonitor;
import cn.com.dhcc.app.core.service.BaseService;
import cn.com.dhcc.app.core.service.Log;
import cn.com.dhcc.app.core.service.Log.LOGTYPE;
import cn.com.dhcc.app.core.tag.CommonStaticCode.SUCCESSFAIL;
import cn.com.dhcc.app.core.util.BeanMapper;
import cn.com.dhcc.app.core.util.CollectionUtil;
import cn.com.dhcc.app.core.util.JsUtil;
import cn.com.dhcc.app.core.util.StorageResourcesUtil;
import cn.com.dhcc.app.pub.CM;
import cn.com.dhcc.app.pub.core.consts.DdiProperties;
import cn.com.dhcc.app.pub.core.util.CPUReader;
import cn.com.dhcc.app.pub.core.util.JVMReader;
import cn.com.dhcc.ddi.datasource.service.DataSourceDBService;
import cn.com.dhcc.ddi.datasource.service.DataSourceService;
import cn.com.dhcc.ddi.datasource.vo.DataSourceVo;
import cn.com.dhcc.ddi.monitor.dao.MonitorDao;
import cn.com.dhcc.ddi.monitor.service.compute.CommonCompute;
import cn.com.dhcc.ddi.monitor.service.compute.MonitorComputeInterface;
import cn.com.dhcc.ddi.monitor.vo.Quota;
import cn.com.dhcc.ddi.monitor.vo.QuotaComputeRule;
import cn.com.dhcc.ddi.monitor.vo.QuotaValueHistory;
import cn.com.dhcc.ddi.monitor.vo.QuotaValueOnTime;
import cn.com.dhcc.ddi.monitor.vo.TaskSuccessCountVo;

@Service
public class MonitorService extends BaseService{
	@Autowired
	MonitorDao monitorDao;
	@Autowired
	DataSourceDBService dataSourceDBService;
	@Autowired
	DataSourceService dataSourceService;
	
	public static final ConcurrentHashMap<String, Object> basic_info_key_ksg= new ConcurrentHashMap<String, Object>();
	private final double MB = 1048576;
	private final double GB = MB*1024;
	private static Logger logger = Log.getLogger(LOGTYPE.DDI);
	
	public Map<String, Object> getBasicInfo() throws UnknownHostException, ServiceException, JmxException {
		//get file system usage	
		QuotaValueOnTime fsTarget = getTargetValueByCode("FS_USED_RATE");
		basic_info_key_ksg.put("fsUsedRate", fsTarget);
		
		//get physical memory usage
		QuotaValueOnTime pmTarget = getTargetValueByCode("PM_USED_RATE");
		basic_info_key_ksg.put("pmUsedRate", pmTarget);
		
		//get CPU usage
		QuotaValueOnTime cpuTarget = getTargetValueByCode("CPU_USED_RATE");
		basic_info_key_ksg.put("cpuUsedRate", cpuTarget);
		
		//初始化监控服务
		AbstractMonitorService monitorService = new DefaultJvmMonitor();
		monitorService.initJmxConn(JmxType.SAME, new Object());
	    OsMonitor osInfo = monitorService.monitorOs();
	    JvmMonitor jvmInfo = monitorService.monitorJvm();
				
		//get ip and os info
		InetAddress ip = InetAddress.getLocalHost();
		String os_name = System.getProperty("os.name");
		int numOfCore = Runtime.getRuntime().availableProcessors();
		basic_info_key_ksg.put("ip", ip);
		basic_info_key_ksg.put("os_name", os_name);
		basic_info_key_ksg.put("numOfCore", numOfCore);

		// jdk info
		Properties p = System.getProperties();// 获取当前的系统属性
		String name = p.getProperty("java.vm.name");
		String version = p.getProperty("java.runtime.version");
		basic_info_key_ksg.put("jdk", name + " " + version);
		
		String[] jvmArgs;
		try {
			jvmArgs = JVMReader.getJvmArgs();
		} catch (Exception e) {
			jvmArgs = null;
		}
		
		String xms = "xms未配置";
		String xmx = "xmx未配置";
		String permSize = "permSize未配置";
		String maxPermSize = "maxPermSize未配置";
		if(jvmArgs != null) {
			for (int i = 0; i < jvmArgs.length; i++) {
				if (jvmArgs[i].contains("-Xms")) {
					xms = jvmArgs[i];
				} else if(jvmArgs[i].contains("-Xmx")) {
					xmx = jvmArgs[i];
				} else if (jvmArgs[i].contains("-XX:PermSiz")) {
					permSize = jvmArgs[i];
				} else if (jvmArgs[i].contains("-XX:MaxPermSiz")) {
					maxPermSize = jvmArgs[i];
				}
			}
		}
		// get vm memory
		
	    
	    
        Runtime runtime = Runtime.getRuntime();
        long heapUsed = jvmInfo.getHeap().getUsed();
        long noneHeapUsed = jvmInfo.getPermGen().getUsed();
        
        
		double totalPhysicalMemory = osInfo.getTotalPmSize()/1024;
		double totalVMMemory =  runtime.maxMemory()/GB;
		basic_info_key_ksg.put("xms", xms);
		basic_info_key_ksg.put("xmx", xmx);
		basic_info_key_ksg.put("permSize", permSize);
		basic_info_key_ksg.put("maxPermSize", maxPermSize);
		basic_info_key_ksg.put("heapUsed", heapUsed);
		basic_info_key_ksg.put("noneHeapUsed", noneHeapUsed);
		basic_info_key_ksg.put("totalPhysicalMemory", round(totalPhysicalMemory, 2));
		basic_info_key_ksg.put("totalVMMemory", round(totalVMMemory, 2));

		// get gc count
		long totalGarbageCollections = 0;

		for (GarbageCollectorMXBean gc : ManagementFactory
				.getGarbageCollectorMXBeans()) {

			long count = gc.getCollectionCount();

			if (count >= 0) {
				totalGarbageCollections += count;
			}
		}
		basic_info_key_ksg.put("gcCount", totalGarbageCollections);

		// get thread info
		//ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
		//int threadCount = threadBean.getThreadCount();
		//int daemonThreadCount = threadBean.getDaemonThreadCount();
		int threadCount = jvmInfo.getThreadCount();
		int daemonThreadCount = jvmInfo.getDaemonThreadCount();
		basic_info_key_ksg.put("threadCount", threadCount);
		basic_info_key_ksg.put("daemonThreadCount", daemonThreadCount);
		
		return basic_info_key_ksg;
	}
	
	private QuotaValueOnTime getTargetValueByCode(String code) throws ServiceException {
		try {
			List<QuotaValueOnTime> list = monitorDao.getQuotaValueOnTimeByQuotaCode(code);
			QuotaValueOnTime target = new QuotaValueOnTime();
			for(QuotaValueOnTime q: list) {
				if(q.getNeedAlert() != null) {
					target = q;
					break;
				}
			}
			
			return target;
		} catch (DaoException e) {
			throw new ServiceException("根据指标编码获取指标实时值错误", e);
		}
	}
	
	private double round(double value, int places) {
	    BigDecimal bd = new BigDecimal(value);
	    bd = bd.setScale(places, RoundingMode.HALF_UP);
	    return bd.doubleValue();
	}
	
	/**
	 * 定时任务，收集基本系统信息
	 * @throws ServiceException 
	 * @throws InterruptedException 
	 * @throws JmxException 
	 * @throws Exception 
	 */
	public synchronized void checkBasicInfo() throws IOException, ServiceException, InterruptedException, NumberFormatException, JmxException {
		try {
			String path = DdiProperties.APP_UPLOAD_FILEDATA_PATH;
			
			
		    int fsUsedRate = StorageResourcesUtil.getFileSystemUsedRate(path);
		    monitorDao.insertQuotaValueOnTime("FS_USED_RATE", fsUsedRate+"", null, new Date());
		    
		    //add physical memory
		    //OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
		    AbstractMonitorService monitorService = new DefaultJvmMonitor();
		    monitorService.initJmxConn(JmxType.SAME, new Object());
		    OsMonitor osInfo = monitorService.monitorOs();
		    
		    long freeMemorySize = osInfo.getFreePmSize();
		    long totalMemorySize = osInfo.getTotalPmSize();
		    int pmUsedRate = (int)(100.0 * (totalMemorySize - freeMemorySize) / totalMemorySize);
		    monitorDao.insertQuotaValueOnTime("PM_USED_RATE", pmUsedRate+"", null, new Date());
		    
		    //add CPU used rate
		    double cpuRatio = round(CPUReader.getCPURatio(), 1);
		    monitorDao.insertQuotaValueOnTime("CPU_USED_RATE", cpuRatio+"", null, new Date());
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
	    
	    
	    //add JVM memory usage
//	    MemoryMXBean memMXBean = ManagementFactory.getMemoryMXBean();
//		MemoryUsage heap = memMXBean.getHeapMemoryUsage();
//		MemoryUsage noneHeap = memMXBean.getNonHeapMemoryUsage();
//		long heapUsed = heap.getUsed();
//		long noneHeapUsed = noneHeap.getUsed();
//		insertQuotaValueOnTime("H_USED_RATE", heapUsed+"", null, new Date());
//		insertQuotaValueOnTime("NH_USED_RATE", noneHeapUsed+"", null, new Date());	
	}
	
	/**
	 * 后台自动告警处理：针对需要发邮件的进行发邮件,已处理的转移到历史记录中
	 * 建议单独线程处理
	 * @throws NiceException 
	 */
	public void monitorOnTimeValueHandle() throws NiceException{
		List<Quota> quotas = null;
		try {
			quotas = CM.getDao().listModule("获取所有监控指标",Quota.class, null);
			if(CollectionUtil.hasElement(quotas)){
				for (Quota quota:quotas) {
					String code = quota.getCode().toUpperCase();
					QUOTA enumQuota = QUOTA.valueOf(QUOTA.class, code);
					logger.info("开始处理指标数据[("+enumQuota.getCode()+")"+enumQuota.getDesc()+"]...");
					
					Class<? extends MonitorComputeInterface> handlerClass = enumQuota.getHandler();
					if(handlerClass==null){
						logger.info("使用默认处理逻辑处理指标[("+enumQuota.getCode()+")"+enumQuota.getDesc()+"]...");
						handlerClass =  CommonCompute.class;
					}
					MonitorComputeInterface handler;
					try {
						handler = CM.getBean(handlerClass);
					} catch (Exception e1) {
						logger.error("实例化告警处理器失败",e1);
						return;
					}
					try {
						//按ownerId,时间倒序排序的指标值
						List<QuotaValueOnTime> onTimes = monitorDao.getQuotaValueOnTimeByQuotaCode(quota.getCode());
						if(onTimes==null||onTimes.size()==0)continue;
						QuotaComputeRule computeRule = null;
						computeRule = monitorDao.getQuotaRuleByRuleSn(quota.getRuleSug());
						handler.moveToHistory(onTimes, quota, computeRule);
						if(computeRule!=null){
							logger.info("开始对指标[("+enumQuota.getCode()+")"+enumQuota.getDesc()+"]进行阈值计算...");
							handler.computeAlert(onTimes, quota, computeRule);
						}
					} catch (Exception e) {
						logger.error("处理指标[("+enumQuota.getCode()+")"+enumQuota.getDesc()+"]出错",e);
						continue;
					}
					
				}
			}
		} catch (DaoException e) {
			logger.error("监控处理异常,请联系管理员!");
			throw new NiceException("告警处理异常！");
		}
	}
	
	public boolean expressCompute(String toComputeJsExpress) throws NiceException{
    	try {
			return (Boolean) JsUtil.simpleComputeExpress(toComputeJsExpress);
		} catch (ScriptException e) {
			throw new NiceException("告警计算出错！");
		}
    }
	
	/**
	 * 将实时数据转移到历史数据
	 * @param quotaValueOnTime
	 * @throws ServiceException
	 */
	public void moveOnTimeMonitorToHistory(final QuotaValueOnTime quotaValueOnTime ) throws ServiceException{
//		quotaValueOnTime.setStatus(QuotaHadlerStatus.HAS_HANDLER.getCode());
		final QuotaValueHistory history = new QuotaValueHistory();
		BeanMapper.copy(quotaValueOnTime, history);
		try {
			CM.getDao().doInSingleTransationCircle("将监控数据转移到历史", new SingleTransationCircleWithOutResult() {
				@Override
				public void actionInCircle() throws RuntimeException {
					try {
						CM.getDao().deleteModuleById("删除实时监控数据", quotaValueOnTime);
						CM.getDao().insertModule("插入监控历史数据", history);
					} catch (Exception e) {
						throw new RuntimeException("将实时数据转移到历史失败");
					}
					
				}
			});
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
	}
	
	// -----定时任务调用
	public synchronized void checkPublishStatus() throws ServiceException,
			NiceException {
		try {

			List<DataSourceVo> dataSources = dataSourceService.getDsList();
			if (CollectionUtil.hasElement(dataSources)) {
				for (Iterator<DataSourceVo> iterator = dataSources.iterator(); iterator
						.hasNext();) {
					DataSourceVo dataSource = iterator.next();
					String ds_id = dataSource.getId();
					boolean statusOk = dataSourceDBService
							.dsStatusOk(dataSource);

					String status;
					if (statusOk) {
						status = SUCCESSFAIL.SUCCESS.getStatus();
					} else {
						status = SUCCESSFAIL.FAIL.getStatus();
					}

					monitorDao.insertQuotaValueOnTime("DS_CONN_STATUS", status,
							ds_id, new Date());
				}
			}
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
	}
		
	public void deleteHistory() throws ServiceException {
		try {
			monitorDao.deleteHistory();
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
	}
	
	public List<DataSourceVo> getDsStatus(WhereCondition searchData) throws ServiceException {
		try {
			return monitorDao.getDsStatus(searchData);
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
	}
	
	public List<TaskSuccessCountVo> getTaskMonitorCount(String taskId) throws ServiceException {
		try {
			return monitorDao.getTaskMonitorCount(taskId);
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
	}
}
